/*
 * Copyright (C) 2008 GeoScheduler Team, as stated on <http://www.geoscheduler.org/authors>.
 * 
 * This file is part of GeoScheduler.
 * 
 * GeoScheduler is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GeoScheduler is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with GeoScheduler. If not, see <http://www.gnu.org/licenses/>.
 */
package org.geoscheduler.security.authorize.impl;

import org.geoscheduler.commons.msgs.types.SecurityMsgs;
import org.geoscheduler.commons.msgs.types.TokenMsgs;
import org.geoscheduler.commons.tools.SecurityTools;
import org.geoscheduler.dao.TokenDAO;
import org.geoscheduler.exceptions.JSONWrappedException;
import org.geoscheduler.model.Token;
import org.geoscheduler.security.authorize.Authorizer;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * Implementation of {@link Authorizer} interface for authorization of JSON requests.
 *
 * @author srakyi
 */
public class JSONAuthorizer implements Authorizer {
	private static final String JSON_AUTH_TYPE = "JSONAuth";
	private static final String JSON_TOKEN = "token";
	private static final String JSON_CHECKSUM = "checksum";
	
	private TokenDAO tokenDAO;
	
	public JSONAuthorizer(TokenDAO tokenDAO) {
		super();
		this.tokenDAO = tokenDAO;
	}

	public boolean isApplicableTo(String auth) {
		try {
			checkAuthorizeHeader(auth);
		} catch (JSONException e) {
			return false;
		}
		
		return true;
	}

	/**
	 * @see org.geoscheduler.security.authorize.Authorizer#authorize(java.lang.String, java.lang.Object)
	 *
	 * Checks authorization on given data.
	 * If everything is ok, method returns, else {@link JSONWrappedException} is thrown.
	 * 
	 * TODO is marshalling/unmarshalling to/from JSON stable? Will we have items in same order? (this is important to make this work, else checksums won't work)
	 * 
	 * @param auth	Content of authorization field.
	 * @param data	Data we are authorizing (we're going to check checksum of this).
	 * @throws JSONException
	 */
	public Token authorize(String auth, Object json) throws JSONException {
		checkAuthorizeHeader(auth);
		auth = auth.replaceFirst("^" + JSON_AUTH_TYPE + " ", "").trim();
		JSONObject jsonAuth = new JSONObject(auth);
		
		Token token = tokenDAO.loadById(jsonAuth.getString(JSON_TOKEN));
		if (token == null) {
			throw new JSONWrappedException(TokenMsgs.INVALID_TOKEN, jsonAuth.getString(JSON_TOKEN));
		}
		
		String data;
		if (json == null) {
			// json data is null in GET requests, we'll use token id instead .. not very nice
			data = token.getId();
		} else {
			data = json.toString();
		}
		
		String checksum = jsonAuth.getString(JSON_CHECKSUM);
		if ((checksum == null) || (!checksum.equals(SecurityTools.computeChecksum(token, data)))) {
			throw new JSONWrappedException(SecurityMsgs.CHECKSUM_DOESNT_MATCH);
		}
		
		return token;
	}
	
	/**
	 * Checks whether given Authorize header can be handled by this authorizer.
	 * 
	 * @param auth	Authorize header content. Null not expected.
	 * 
	 * @throws JSONException
	 */
	private void checkAuthorizeHeader(String auth) throws JSONException {
		if (auth == null) {
			// FIXME [anyone] how can we return HTTP 401 instead?
			throw new JSONWrappedException(SecurityMsgs.AUTHORIZATION_NEEDED);
		}
		if (!auth.trim().startsWith(JSON_AUTH_TYPE + " ")) {
			// FIXME [anyone] how can we return HTTP 401 instead?
			throw new JSONWrappedException(SecurityMsgs.AUTHORIZATION_TYPE_NOT_SUPPORTED);
		}
	}

}
